/**
 * Jest测试框架介绍与使用方法
 * ===========================
 *
 * Jest是一个流行的JavaScript测试框架，它简单易用，可以用于测试React、Vue、Angular等前端框架以及Node.js应用。
 *
 * 1. 安装Jest
 * -----------
 * npm install --save-dev jest  // 使用npm安装
 * yarn add --dev jest         // 使用yarn安装
 *
 * 2. 配置package.json
 * ------------------
 * {
 *   "scripts": {
 *     "test": "jest",
 *     "test:watch": "jest --watch",
 *     "test:coverage": "jest --coverage"
 *   }
 * }
 *
 * 3. 创建测试文件
 * --------------
 * Jest会自动查找以下格式命名的文件:
 * - `.JavaScript数组遍历高阶函数.js`结尾的文件
 * - `.spec.js`结尾的文件
 * - 位于`__tests__`目录下的任何js文件
 *
 * 4. 运行测试
 * ----------
 * npm test                    // 运行所有测试
 * npm test -- --watch         // 监视模式，监控文件变化并自动运行测试
 * npm test -- path/to/JavaScript数组遍历高阶函数.js // 只运行特定文件的测试
 * npm test -- -t "test name"  // 运行匹配特定名称模式的测试
 * npm test -- --coverage      // 显示测试覆盖率报告
 *
 * 5. Jest基本语法
 * -------------
 * // 分组测试
 * describe('测试组名称', () => {
 *   // 在所有测试前执行一次
 *   beforeAll(() => { ... });
 *
 *   // 在每个测试前执行
 *   beforeEach(() => { ... });
 *
 *   // 测试用例
 *   test('测试名称', () => {
 *     expect(实际值).toBe(期望值);
 *   });
 *
 *   // 在每个测试后执行
 *   afterEach(() => { ... });
 *
 *   // 在所有测试后执行一次
 *   afterAll(() => { ... });
 * });
 *
 * 6. 常用匹配器(Matchers)
 * --------------------
 * expect(value).toBe(2);                // 精确相等 (===)
 * expect(object).toEqual({name: '测试'}); // 对象内容相等
 * expect(value).toBeTruthy();           // 真值检查
 * expect(value).toBeFalsy();            // 假值检查
 * expect(value).toBeGreaterThan(3);     // 数字比较
 * expect(value).toBeLessThan(5);        // 数字比较
 * expect(array).toContain('item');      // 数组包含
 * expect(string).toMatch(/pattern/);    // 字符串匹配
 * expect(() => { fn() }).toThrow();     // 异常检测
 *
 * 7. 异步测试
 * ----------
 * // Promise测试
 * test('异步测试', () => {
 *   return fetchData().then(data => {
 *     expect(data).toBe('data');
 *   });
 * });
 *
 * // async/await测试
 * test('异步测试', async () => {
 *   const data = await fetchData();
 *   expect(data).toBe('data');
 * });
 *
 * // 回调函数测试
 * test('异步测试', done => {
 *   fetchData(data => {
 *     expect(data).toBe('data');
 *     done();
 *   });
 * });
 *
 * 8. 模拟函数(Mock)
 * ---------------
 * // 创建模拟函数
 * const mockFn = jest.fn();
 * mockFn.mockReturnValue(42);          // 设置返回值
 * mockFn.mockImplementation(() => 42); // 设置实现
 *
 * // 断言
 * expect(mockFn).toHaveBeenCalled();           // 函数被调用
 * expect(mockFn).toHaveBeenCalledWith(1, 2);   // 用特定参数调用
 * expect(mockFn).toHaveBeenCalledTimes(2);     // 调用次数
 */

// array-methods.JavaScript数组遍历高阶函数.js
// 用于测试JavaScript数组方法的单元测试

describe('JavaScript数组方法测试', () => {
  // 测试数据
  const numbers = [1, 2, 3, 4, 5];
  const users = [
    { id: 1, name: '张三', age: 25, active: true },
    { id: 2, name: '李四', age: 30, active: false },
    { id: 3, name: '王五', age: 22, active: true },
    { id: 4, name: '赵六', age: 35, active: true }
  ];

  // 测试 map() 方法
  test('map() 方法应该返回一个新数组，每个元素都是原数组元素调用函数后的结果', () => {
    const doubled = numbers.map(num => num * 2);
    expect(doubled).toEqual([2, 4, 6, 8, 10]);

    const userNames = users.map(user => user.name);
    expect(userNames).toEqual(['张三', '李四', '王五', '赵六']);
  });

  // 测试 forEach() 方法
  test('forEach() 方法应该遍历数组中的每个元素并执行回调函数', () => {
    let sum = 0;
    numbers.forEach(num => {
      sum += num;
    });
    expect(sum).toBe(15);

    let activeUsers = [];
    users.forEach(user => {
      if (user.active) {
        activeUsers.push(user.name);
      }
    });
    expect(activeUsers).toEqual(['张三', '王五', '赵六']);
  });

  // 测试 filter() 方法
  test('filter() 方法应该返回一个满足条件的元素组成的新数组', () => {
    const evenNumbers = numbers.filter(num => num % 2 === 0);
    expect(evenNumbers).toEqual([2, 4]);

    const activeUsers = users.filter(user => user.active);
    expect(activeUsers.length).toBe(3);
    expect(activeUsers[0].name).toBe('张三');
  });

  // 测试 reduce() 方法
  test('reduce() 方法应该将数组中的元素累加成单个值', () => {
    const sum = numbers.reduce((total, num) => total + num, 0);
    expect(sum).toBe(15);

    const totalAge = users.reduce((total, user) => total + user.age, 0);
    expect(totalAge).toBe(112);

    // 使用reduce创建对象
    const nameById = users.reduce((obj, user) => {
      obj[user.id] = user.name;
      return obj;
    }, {});
    expect(nameById).toEqual({1: '张三', 2: '李四', 3: '王五', 4: '赵六'});
  });

  // 测试 find() 方法
  test('find() 方法应该返回满足条件的第一个元素', () => {
    const found = numbers.find(num => num > 3);
    expect(found).toBe(4);

    const user = users.find(user => user.age > 30);
    expect(user.name).toBe('赵六');

    const notFound = numbers.find(num => num > 10);
    expect(notFound).toBeUndefined();
  });

  // 测试 findIndex() 方法
  test('findIndex() 方法应该返回满足条件的第一个元素的索引', () => {
    const foundIndex = numbers.findIndex(num => num > 3);
    expect(foundIndex).toBe(3);

    const userIndex = users.findIndex(user => user.name === '王五');
    expect(userIndex).toBe(2);

    const notFoundIndex = numbers.findIndex(num => num > 10);
    expect(notFoundIndex).toBe(-1);
  });

  // 测试 some() 方法
  test('some() 方法应该测试数组中是否至少有一个元素通过测试', () => {
    const hasEven = numbers.some(num => num % 2 === 0);
    expect(hasEven).toBe(true);

    const hasOldUser = users.some(user => user.age > 40);
    expect(hasOldUser).toBe(false);

    const hasActiveUser = users.some(user => user.active);
    expect(hasActiveUser).toBe(true);
  });

  // 测试 every() 方法
  test('every() 方法应该测试数组中的所有元素是否都通过测试', () => {
    const allPositive = numbers.every(num => num > 0);
    expect(allPositive).toBe(true);

    const allEven = numbers.every(num => num % 2 === 0);
    expect(allEven).toBe(false);

    const allActive = users.every(user => user.active);
    expect(allActive).toBe(false);
  });

  // 测试 flatMap() 方法
  test('flatMap() 方法应该先映射每个元素，然后将结果扁平化为新数组', () => {
    const sentences = ['JavaScript 数组', '方法测试'];
    const words = sentences.flatMap(sentence => sentence.split(' '));
    expect(words).toEqual(['JavaScript', '数组', '方法测试']);

    // 比较 map 和 flatMap
    const result1 = numbers.map(num => [num, num * 2]);
    expect(result1).toEqual([[1, 2], [2, 4], [3, 6], [4, 8], [5, 10]]);

    const result2 = numbers.flatMap(num => [num, num * 2]);
    expect(result2).toEqual([1, 2, 2, 4, 3, 6, 4, 8, 5, 10]);
  });
});
